#include "SimulateI2C.h"

void SimulateI2C_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);                    // 总线时钟
    GPIO_InitStructure.GPIO_Pin = SimulateI2C_SCL_PIN | SimulateI2C_SDA_PIN; // 选择IO组的端口引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;                         // 开漏输出(外部I2C总线加上拉电阻)
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(SimulateI2C_PORT, &GPIO_InitStructure);                          // IO组初始化
    GPIO_SetBits(SimulateI2C_PORT, SimulateI2C_SCL_PIN | SimulateI2C_SDA_PIN); // I2C总线默认为高电平, 释放总线
}

void SimulateI2C_WriteSCL(uint8_t BitVal)
{
    GPIO_WriteBit(SimulateI2C_PORT, SimulateI2C_SCL_PIN, (BitAction)BitVal);
    delay_us(10); // 主频快可以适当延时
}

void SimulateI2C_WriteSDA(uint8_t BitVal)
{
    GPIO_WriteBit(SimulateI2C_PORT, SimulateI2C_SDA_PIN, (BitAction)BitVal);
    delay_us(10);
}

uint8_t SimulateI2C_ReadSDA(void)
{
    uint8_t BitVal;
    BitVal = GPIO_ReadInputDataBit(SimulateI2C_PORT, SimulateI2C_SDA_PIN);
    // delay_us(10);
    return BitVal;
}

/**
 * @attention 起始:SCL(1)期间,SDA从1--→0跳变
 * @attention (1)释放总线;(2)SDA(0);(3)SCL(0)
 * @note 只能由主机产生起始
 */
void SimulateI2C_Start(void)
{
    SimulateI2C_WriteSDA(1);
    SimulateI2C_WriteSCL(1);
    SimulateI2C_WriteSDA(0);
    SimulateI2C_WriteSCL(0); // SCL(0)(方便SDA电平可切换)
}

/**
 * @attention 终止: SCL(1)期间，SDA由0向1跳变
 * @note 只能由主机产生终止
 */
void SimulateI2C_Stop(void)
{
    SimulateI2C_WriteSDA(0);
    SimulateI2C_WriteSCL(1);
    SimulateI2C_WriteSDA(1);
}

/**
 * @brief 主机发送1Byte,先发送高位
 * @attention SCL(0)期间,主机写SDA; SCL(1)期间,从机读取数据位。
 */
void SimulateI2C_SendByte(uint8_t b)
{
    uint8_t i;
    // Start()已经拉低SCL(0)

    for (i = 0; i < 8; i++)
    {
        SimulateI2C_WriteSDA((b & (0x80 >> i)) / (0x80 >> i));
        SimulateI2C_WriteSCL(1);
        SimulateI2C_WriteSCL(0);
    }
}

/**
 * @brief 主机(发送1Byte后)接收从机应答: 0(从机应答),1(从机非应答)
 * @attention 主机接收从机应答前需要释放SDA(1),在SCL(1)期间读取SDA
 * @attention
 */
uint8_t SimulateI2C_ReceiveAck(void)
{
    uint8_t AckBit;
    SimulateI2C_WriteSDA(1); // 主机接收前需要释放SDA(1)
    SimulateI2C_WriteSCL(1);
    AckBit = SimulateI2C_ReadSDA(); //
    SimulateI2C_WriteSCL(0);        //
    return AckBit;
}

/**
 * @brief 主机接收1Byte,先读高位
 * @attention 接收前主机需要释放SDA(1)
 * @attention SCL(0)期间,从机写SDA;SCL(1)期间,主机在读取数据位,
 * @return uint8_t
 */
uint8_t SimulateI2C_ReceiveByte(void)
{
    uint8_t i;
    uint8_t temp = 0x00;
    SimulateI2C_WriteSDA(1); // 接收前主机需要释放SDA(1)
    for (i = 0; i < 8; i++)
    {
        SimulateI2C_WriteSCL(1);
        if (SimulateI2C_ReadSDA() == 1)
        {
            temp = temp | (0x80 >> i);
        }
        SimulateI2C_WriteSCL(0);
    }
    return temp;
}

/**
 * @brief 主机(接收1Byte后)发送应答:SCL(1)期间写SDA
 * @attention 主机接收(1Byte)后;在下一个时钟发送1bit,0(应答),1(非应答)
 * @attention 如果主机不继续接收更多字节,那么主机应该回答1以提示从机准备接受Stop信号
 */
void SimulateI2C_SendAck(uint8_t AckBit)
{
    SimulateI2C_WriteSDA(AckBit);
    SimulateI2C_WriteSCL(1);
    SimulateI2C_WriteSCL(0);
}
